#pragma once

#include "JsoncppHandler.h"

#include <string>
#include <type_traits>
#include <memory>
#include <json/json.h>
#include <optional>

#include "../Log/TraceLog.h"

struct JsonReflectionBase
{
public: 
    bool parseWebJsonInput(std::shared_ptr<Json::Value> webInputJson) 
    {
        bool hasError = false; 
        return !hasError; 
    }

    std::shared_ptr<Json::Value> genarateWebJsonOutput() const { 
        std::shared_ptr<Json::Value> pJson = std::make_shared<Json::Value>();
        bool succFlag = true; 
        if( succFlag ) 
        { 
            return pJson; 
        } 
        return nullptr; 
    }
};

///////////////////////////////////////////////////////////////////////////////////////////////////
template<typename T>
auto JsonGetImp( T& value, std::shared_ptr<Json::Value> pJson, const std::string& key, bool* hasError) -> typename std::enable_if<std::is_integral<T>::value || std::is_same<T, double>::value || std::is_same<T, float>::value || std::is_same<T, std::string>::value || std::is_same<T, bool>::value, void>::type
{
    value = JsoncppHandler::JsonGet<T, decltype(key)>(*pJson, key, hasError);
}

template<typename T>
auto JsonGetImp( std::vector<T>& value, std::shared_ptr<Json::Value> pJson, const std::string& key, bool* hasError) -> typename std::enable_if<std::is_integral<T>::value || std::is_same<T, std::string>::value, void>::type
{
    int size = (*pJson)[key].size();
    for (int i = 0; i < size; i++)
    {
        T tmp = JsoncppHandler::JsonGet<T, decltype(i)>((*pJson)[key], i, hasError);
        value.push_back(tmp);
    }
}

template<typename T>
auto JsonGetImp( T& value, std::shared_ptr<Json::Value> pJson, const std::string& key, bool* hasError) -> typename std::enable_if< std::is_base_of<JsonReflectionBase, T>::value,void>::type
{
    std::shared_ptr<Json::Value> pSubJson = std::make_shared<Json::Value>((*pJson)[key]);
    bool ret = value.parseWebJsonInput(pSubJson);
	if (!ret && hasError != nullptr)
	{
		*hasError = !ret;
	}
}

template<typename T>
auto JsonGetImp( std::vector<T>& value, std::shared_ptr<Json::Value> pJson, const std::string& key, bool* hasError) -> typename std::enable_if< std::is_base_of<JsonReflectionBase,T>::value, void>::type
{
    int size = (*pJson)[key].size();
    for (int i = 0; i < size; i++)
    {
        T tmp;
        std::shared_ptr<Json::Value> pSubJson = std::make_shared<Json::Value>((*pJson)[key][i]);
        bool ret = tmp.parseWebJsonInput(pSubJson);
	    if (!ret && hasError != nullptr)
	    {
		    *hasError = !ret;
	    }
        value.push_back(tmp);
    }

}

template<typename T>
void JsonGetImp( std::optional<T>& value, std::shared_ptr<Json::Value> pJson, const std::string& key, bool* hasError)
{
    if ((*pJson)[key].isNull())
    {
        value = std::nullopt;
    }
    else
    {
        value = T();
        JsonGetImp(*value, pJson, key, hasError);
    }
}
///////////////////////////////////////////////////////////////////////////////////////////////////
template<typename T>
auto JsonAddImp(std::shared_ptr<Json::Value> pJson, const std::string& key, T value) -> typename std::enable_if<std::is_integral<T>::value || std::is_floating_point<T>::value || std::is_same<T, std::string>::value || std::is_same<T, bool>::value, bool>::type
{
    (*pJson)[key] = value;
    return true;
}

template<typename T>
auto JsonAddImp(std::shared_ptr<Json::Value> pJson, const std::string& key, std::optional<T> value)
{
    if (value.has_value()) {
        return JsonAddImp(pJson, key, value.value());
    }
    return true;
}

template<typename T>
auto JsonAddImp(std::shared_ptr<Json::Value> pJson, const std::string& key, T value) -> typename std::enable_if<std::is_base_of<JsonReflectionBase, T>::value, bool>::type
{
    auto json = value.genarateWebJsonOutput();
    (*pJson)[key] = *json;
    return true;
}

template<typename T>
auto JsonAddImp(std::shared_ptr<Json::Value> pJson, const std::string& key, std::vector<T> value) -> typename std::enable_if<!std::is_base_of<JsonReflectionBase, T>::value, bool>::type
{
    Json::Value array;
    for (auto &it : value)
    {
        array.append(it);
    }
    
    (*pJson)[key] = array;
    return true;
}

template<typename T>
auto JsonAddImp(std::shared_ptr<Json::Value> pJson, const std::string& key, std::vector<T> value) -> typename std::enable_if<std::is_base_of<JsonReflectionBase, T>::value, bool>::type
{
    Json::Value array;
    for (auto &it : value)
    {
        auto json = it.genarateWebJsonOutput();
        array.append(*json);
    }
    
    (*pJson)[key] = array;
    return true;
}